package com.humanminoritygame.model.mock;

import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import com.humanminoritygame.model.AnswerType;
import com.humanminoritygame.model.ResultSummary;
import com.humanminoritygame.model.Room;
import com.humanminoritygame.model.Round;
import com.humanminoritygame.model.Subscription;
import com.humanminoritygame.model.UserRoundResult;
import com.humanminoritygame.model.UserRoundResultRepo;

public class MockUserRoundResultRepo implements UserRoundResultRepo {
  private SortedSet<UserRoundResult> userRoundResults = new TreeSet<UserRoundResult>();

  public MockUserRoundResultRepo() {
  }

  @Override
  public Set<UserRoundResult> getUserRoundResults(Round round) {
    SortedSet<UserRoundResult> results = new TreeSet<UserRoundResult>();
    for (UserRoundResult urr : userRoundResults) {
      if (urr.getRound().equals(round)) {
        results.add(urr);
      }
    }
    return results;
  }

  @Override
  public UserRoundResult getLastUserRoundResult(Subscription subscription) {
    Iterator<UserRoundResult> i = getUserRoundResults(subscription).iterator();
    if (!i.hasNext()) {
      return null;
    }
    UserRoundResult last = i.next();
    while (i.hasNext()) {
      UserRoundResult actual = i.next();
      if (actual.compareTo(last) > 0) {
        last = actual;
      }
    }
    return last;
  }

  @Override
  public Set<UserRoundResult> getAllUserRoundResults() {
    return userRoundResults;
  }

  public Set<UserRoundResult> getUserRoundResults(Subscription subscription) {
    TreeSet<UserRoundResult> results = new TreeSet<UserRoundResult>();
    for (UserRoundResult urr : userRoundResults) {
      if (urr.getSubscription().equals(subscription)) {
        results.add(urr);
      }
    }
    return results;
  }

  @Override
  public void addUserRoundResult(UserRoundResult userRoundResult) {
    userRoundResults.add(userRoundResult);
  }

  @Override
  public UserRoundResult getLastUserRoundResult(Round round) {
    Set<UserRoundResult> userRoundResults = this.getUserRoundResults(round);
    Iterator<UserRoundResult> i = userRoundResults.iterator();
    UserRoundResult last;
    try {
      last = i.next();
    } catch (Exception e) {
      return null;
    }
    while (i.hasNext()) {
      UserRoundResult actual = i.next();
      if (actual.compareTo(last) > 0) {
        last = actual;
      }
    }
    return last;

  }

  @Override
  public UserRoundResult getUserRoundResult(Round round,
      Subscription subscription) {
    for (UserRoundResult userRoundResult : userRoundResults) {
      if (userRoundResult.getRound().equals(round)
          && userRoundResult.getSubscription().equals(subscription)) {
        return userRoundResult;
      }
    }
    return null;
  }

  @Override
  public ResultSummary processResults(Round round) {
    // TODO revisar
    // if (!round.hasElapsed()) {
    // throw new RuntimeException("Results cannot be processed while the round
    // is still active");
    // }
    ResultSummary resp = calculateResult(round);
    setSubscriptionsRoundResults(round, resp);
    round.setSummary(resp);
    return resp;
  }

  private void setSubscriptionsRoundResults(Round round, ResultSummary result) {
    Set<UserRoundResult> roundVotes = getUserRoundResults(round);
    for (UserRoundResult userRoundResult : roundVotes) {
      if (userRoundResult.getAnswer().equals(result.getAnswer())) {
        userRoundResult.addScore(result.getDifference());
      } else {
        userRoundResult.addScore((-1) * result.getDifference());
      }
    }
    if(roundVotes.size() < round.getRoom().getQtyActiveSubscriptions()){
      // TODO no penalizar si todos votaron
      //TODO revisar la funcion de punishment porque estaba penalizando a gente que ya habia votado...!
      punishLazySubscriptions(round, result);
    }

  }

  private void punishLazySubscriptions(Round round, ResultSummary result) {
    for (Subscription subscription : round.getRoom().getActiveSubscriptions()) {
      UserRoundResult last = getLastUserRoundResult(subscription);
      if (last == null || !last.getRound().equals(round)) {
        UserRoundResult punishment = new UserRoundResult(AnswerType.NOANSWER,
            subscription, round);
        punishment.addScore((-1) * result.getDifference());
        addUserRoundResult(punishment);
      }
    }
  }

  private ResultSummary calculateResult(Round round) {
    int As = 0, Bs = 0;
    for (UserRoundResult userRoundResult : getUserRoundResults(round)) {
      if (userRoundResult.getAnswer().equals(AnswerType.A)) {
        As++;
      } else {
        Bs++;
      }
    }
    if (As == Bs) {
      try {
        UserRoundResult last = getLastUserRoundResult(round);
        if (last.getAnswer().equals(AnswerType.A)) {
          As--;
        } else {
          Bs--;
        }
      } catch (Exception e) {
        throw new RuntimeException("As== Bs y last de la ronda es null");
      }
    }
    AnswerType answer;
    if (As > Bs) {
      answer = AnswerType.B;
    } else {
      answer = AnswerType.A;
    }
    //TODO: +rand
    return new ResultSummary(answer, 100 * Math.abs(As - Bs) / (As + Bs), 0);
  }

  public int getPoints(Subscription subscription) {
    int points = 0;
    for (UserRoundResult subs : getUserRoundResults(subscription)) {
      points += subs.getPoints();
    }
    return (int) Math.round(points);
  }

  @Override
  public int getRanking(Subscription subscription) {
    //TODO: hay qe agarrar todos los activeSubscriptions del room y ordenar por "getPoint"
    //y de ahi decir cual es la posicion de este subscription
    Room room = subscription.getRoom();
    Set<Subscription> actives = room.getActiveSubscriptions();
    int points = getPoints(subscription);
    int index = 1;
    for(Subscription s : actives){
      if( !s.equals(subscription) && getPoints(s) > points ){
        index++;
      }
    }
    //TODO: hacerlo porcentual
    return index;   
  }

  @Override
  public boolean hasVoted(Subscription subscription, Round round) {
    // TODO Auto-generated method stub
    return false;
  }

  @Override
  public List<UserRoundResult> getOrderedUserRoundResults(Subscription s) {
    // TODO Auto-generated method stub
    return null;
  }

}
